home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / newtee / newtee.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-10-31  |  8.4 KB  |  367 lines

  1. /* 
  2.  * newtee.c --
  3.  *
  4.  *    Program to take standard input or input from a named file and
  5.  *     transcribe it to both the standard output and the given files.
  6.  *    It is unbuffered.  The writer process is the child of the reader
  7.  *    process.  If the child blocks writing to the files due to a file
  8.  *    server being down, we still want the output on stdout, so the
  9.  *    parent reader process is set up not to block and has the
  10.  *    responsibility to send the data to stdout.
  11.  *
  12.  * Copyright 1990 Regents of the University of California
  13.  * Permission to use, copy, modify, and distribute this
  14.  * software and its documentation for any purpose and without
  15.  * fee is hereby granted, provided that the above copyright
  16.  * notice appear in all copies.  The University of California
  17.  * makes no representations about the suitability of this
  18.  * software for any purpose.  It is provided "as is" without
  19.  * express or implied warranty.
  20.  */
  21.  
  22. #ifndef lint
  23. static char rcsid[] = "$Header: /sprite/src/cmds/newtee/RCS/newtee.c,v 1.4 90/10/31 14:57:55 mgbaker Exp $ SPRITE (Berkeley)";
  24. #endif /* not lint */
  25.  
  26. #include <sprite.h>
  27. #include <stdio.h>
  28. #include <option.h>
  29. #include <signal.h>
  30. #include <sys/file.h>
  31. #include <sys/types.h>
  32. #include <fcntl.h>
  33. #include <errno.h>
  34.  
  35.  
  36. char    in[BUFSIZ];    /* Input data from read process put here. */
  37. char     out[BUFSIZ];    /* Output data for write process put here. */
  38. int    pipedesc[2];    /* Pipe to pass data from read process to write proc. */
  39. int    inFd;        /* File descr. for input to program. */
  40. int    outFiles[20];    /* Files to put output data in. */
  41. int    maxFileIndex = -1;    /* The index of the last output file. */
  42. int    childPID;    /* Pid of write process. */
  43.  
  44. /*
  45.  * Options to program.
  46.  */
  47. Boolean    append = FALSE;    /* Append to output files rather than overwrite them. */
  48. Boolean    keepGoing = FALSE;    /* Always wait for more input (tail -f). */
  49. char    *inputFile = NULL;    /* Get input from file instead of stdin. */
  50.  
  51. Option optionArray[] = {
  52.     {OPT_TRUE, "append", (char *)&append,
  53.     "Append to files rather than overwrite them."},
  54.     {OPT_STRING, "inputFile", (char *)&inputFile,
  55.     "Use this file as input rather than standard input."},
  56.     {OPT_TRUE, "keepGoing", (char *)&keepGoing,
  57.     "Keep trying to read data from input even if the end is reached."},
  58.  
  59. };
  60. int numOptions = sizeof(optionArray) / sizeof(Option);
  61.  
  62. /*
  63.  * Forward declarations.
  64.  */
  65. extern    void    KillChild();
  66. extern    int    CleanupFunc();
  67. extern    void    InputReader();
  68. extern    void    OutputWriter();
  69.  
  70.  
  71. /*
  72.  *----------------------------------------------------------------------
  73.  *
  74.  * main --
  75.  *
  76.  *    Gather arguments and setup reading and writing processes.
  77.  *
  78.  * Results:
  79.  *    None.
  80.  *
  81.  * Side effects:
  82.  *    None.
  83.  *
  84.  *----------------------------------------------------------------------
  85.  */
  86. void
  87. main(argc, argv)
  88.     int argc;
  89.     char *argv[];
  90. {
  91.     int        modeFlags;
  92.  
  93.     argc = Opt_Parse(argc, argv, optionArray, numOptions, 0);
  94.     if (append) {
  95.     modeFlags = O_RDWR | O_APPEND | O_CREAT;
  96.     } else {
  97.     modeFlags = O_RDWR | O_CREAT | O_TRUNC;
  98.     }
  99.     if (argc <= 1) {
  100.     fprintf(stderr, "You must give at least one filename for output.\n");
  101.     exit(1);
  102.     }
  103.  
  104.     /*
  105.      * Gather output files and try to open them all.  Keep their descriptors.
  106.      */
  107.     for ( ; argc > 1; argc--) {
  108.     if (maxFileIndex + 1 == sizeof (outFiles) / sizeof (outFiles[0])) {
  109.         fprintf(stderr, "Can only handle %d output files.\n",
  110.         maxFileIndex + 1);
  111.         exit(1);
  112.     }
  113.     outFiles[maxFileIndex + 1] = open(argv[argc - 1], modeFlags, 0666);
  114.     if (outFiles[maxFileIndex + 1] < 0) {
  115.         fprintf(stderr, "Couldn't open output file %s\n",
  116.             argv[argc - 1]);
  117.     } else {
  118.         maxFileIndex++;
  119.     }
  120.     }
  121.     if (maxFileIndex < 0) {
  122.     perror("Couldn't open any output files");
  123.     exit(1);
  124.     }
  125.     
  126.     /*
  127.      * Now check whether we must open an input file.
  128.      */
  129.     if (inputFile != NULL) {
  130.     inFd = open(inputFile, O_RDONLY, 0);
  131.     if (inFd < 0) {
  132.         perror("Couldn't open input file");
  133.         exit(1);
  134.     }
  135.     } else {
  136.     inFd = 0;        /* Use standard input by default. */
  137.     }
  138.  
  139.     /*
  140.      * Set up the pipe between reader and writer.
  141.      */
  142.     if (pipe(pipedesc) != 0) {
  143.     perror("Couldn't create pipe.");
  144.     exit(1);
  145.     }
  146.  
  147.     /*
  148.      * Don't allow reader process to block since we want it to keep on
  149.      * sending its output to stdout even if a file server is down which
  150.      * could block the writer process.
  151.      */  
  152.     if (fcntl(pipedesc[1], F_SETFL, FNDELAY) == -1) {
  153.     perror("Coulnd't set pipe to non-blocking");
  154.     exit(1);
  155.     }
  156.  
  157.     /*
  158.      * Fork writer process.
  159.      */
  160.     switch (childPID = fork()) {
  161.     case 0: /* child */
  162.     OutputWriter();
  163.     break;
  164.     case -1: /* error */
  165.     perror("Couldn't fork child process");
  166.     exit(1);
  167.     default: /* parent */
  168.     (void) signal(SIGCHLD, CleanupFunc);
  169.     InputReader();
  170.     break;
  171.     }
  172.  
  173.     KillChild();
  174.     exit(0);
  175. }
  176.  
  177.  
  178.  
  179.  
  180. /*
  181.  *----------------------------------------------------------------------
  182.  *
  183.  * InputReader --
  184.  *
  185.  *    Parent process that reads from input to program and copies the
  186.  *    data through a pipe to the child reader process.  This parent is
  187.  *    set up not to block on the child and has the responsibility to
  188.  *    copy its input to stdout so that we see it even if the child
  189.  *    writer process blocks due to server failure.
  190.  *
  191.  * Results:
  192.  *    It never returns.
  193.  *
  194.  * Side effects:
  195.  *    Data read and copied.
  196.  *
  197.  *----------------------------------------------------------------------
  198.  */
  199. void
  200. InputReader()
  201. {
  202.     int        readCount;    /* Count of data read into program. */
  203.     int        writeCount;    /* Count of data written to pipe, etc. */
  204.  
  205.  
  206.     (void) close(pipedesc[0]);
  207.  
  208.     for ( ; ; ) {
  209.     /*
  210.      * Read data into program.
  211.      */
  212.     readCount = read(inFd, &in, sizeof (in));
  213.     if (readCount < 0) {
  214.         perror("Couldn't read input");
  215.         KillChild();
  216.         exit(1);
  217.     }
  218.     /*
  219.      * Don't accept end-of-file if we're in "tail -f" mode.
  220.      */
  221.     if (readCount == 0 && keepGoing) {
  222.         continue;
  223.     } else if (readCount == 0) {    /* end of file */
  224.         KillChild();
  225.         exit(0);
  226.     }
  227.  
  228.     /*
  229.      * Copy input to stdout.
  230.      */
  231.     writeCount = write(2, &in, readCount);
  232.     if (writeCount < 0) {
  233.         perror("Couln't write to stdout");
  234.         KillChild();
  235.         exit(1);
  236.     }
  237.     if (writeCount < readCount) {
  238.         perror("Couldn't complete write to stdout");
  239.         KillChild();
  240.         exit(1);
  241.     }
  242.  
  243.     /*
  244.      * Now copy the input to the non-blocking pipe.
  245.      */
  246.     writeCount = write(pipedesc[1], &in, readCount);
  247.     if (writeCount < 0 && errno != EWOULDBLOCK) {
  248.         perror("Couln't write to pipe");
  249.         KillChild();
  250.         exit(1);
  251.     }
  252.     if (writeCount < readCount) {
  253.         perror("Couldn't complete write to pipe");
  254.         KillChild();
  255.         exit(1);
  256.     }
  257.     }
  258.     KillChild();
  259.     exit(0);
  260. }
  261.  
  262.  
  263. /*
  264.  *----------------------------------------------------------------------
  265.  *
  266.  * OutputWriter --
  267.  *
  268.  *    Child process that reads from pipe and copies the
  269.  *    data to the output files.
  270.  *
  271.  * Results:
  272.  *    It never returns.
  273.  *
  274.  * Side effects:
  275.  *    Data read and copied.
  276.  *
  277.  *----------------------------------------------------------------------
  278.  */
  279. void
  280. OutputWriter()
  281. {
  282.     int        readCount;    /* Count of bytes read from pipe. */
  283.     int        writeCount;    /* Count of bytes written to files. */
  284.     int        theFile;    /* The current output file descriptor. */
  285.  
  286.     (void) close(pipedesc[1]);
  287.  
  288.     for ( ; ; ) {
  289.     /*
  290.      * Read data from parent process.
  291.      */
  292.     readCount = read(pipedesc[0], &out, sizeof (out));
  293.     if (readCount < 0) {
  294.         perror("Couldn't read from pipe");
  295.         _exit(1);
  296.     }
  297.     if (readCount == 0) {
  298.         exit(0);
  299.     }
  300.  
  301.     /*
  302.      * Write the data to each output file.
  303.      */
  304.     for (theFile = 0; theFile <= maxFileIndex; theFile++) {
  305.         writeCount = write(outFiles[theFile], &out, readCount);
  306.         if (writeCount < 0) {
  307.         perror("Couldn't write output to file");
  308.         _exit(1);
  309.         }
  310.         if (writeCount < readCount) {
  311.         perror("Couldn't write all of output");
  312.         _exit(1);
  313.         }
  314.     }
  315.     }
  316.     _exit(0);
  317. }
  318.  
  319.  
  320.  
  321. /*
  322.  *----------------------------------------------------------------------
  323.  *
  324.  * CleanupFunc --
  325.  *
  326.  *    Called from signal handler by parent if it's child dies.
  327.  *    If the child dies, the parent should too.  At least, that's the
  328.  *    current idea.
  329.  *
  330.  * Results:
  331.  *    None.
  332.  *
  333.  * Side effects:
  334.  *    Death.
  335.  *
  336.  *----------------------------------------------------------------------
  337.  */
  338. int
  339. CleanupFunc()
  340. {
  341.     exit(1);
  342. }
  343.  
  344.  
  345. /*
  346.  *----------------------------------------------------------------------
  347.  *
  348.  * KillChild --
  349.  *
  350.  *    Called by the parent process if it's going to die.  We want the
  351.  *    child to die too in that case.
  352.  *
  353.  * Results:
  354.  *    None.
  355.  *
  356.  * Side effects:
  357.  *    Death.
  358.  *
  359.  *----------------------------------------------------------------------
  360.  */
  361. void
  362. KillChild()
  363. {
  364.     (void) kill(childPID, SIGKILL);
  365.     return;
  366. }
  367.